﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Engine.Dtd.Schema;

namespace Engine.Sgml.Parser {
    internal sealed class CDataReader : IElementReader {
        private StringBuilder builder;

        public CDataReader(StringBuilder builder) {
            Guard.ArgumentNotNull(builder, "builder");
            this.builder = builder;
            }

        public SElement Read(SgmlStreamReader reader, SElement parent, Context context) {
            builder.Clear();
            SElement node = parent.LastNode;
            if (node != null && node.NodeType == SgmlNodeType.WhiteSpace) {
                builder.Append(node.Value);
                parent.Nodes.Pop();
                }
            while (reader.CurrentChar != Characters.EndOfFile) {
                // See if this is the end tag for this CDATA node.
                if (reader.CurrentChar == Characters.StartTagMarkup && reader.PeekChar() == Characters.ClosingTagMarkup) {
                    // Consume '<' and '/'  symbols
                    reader.ReadChar();
                    reader.ReadChar();
                    string elementName = context.NameReader.ReadPrefixNameToken(reader);
                    if (elementName != null && reader.SkipWhitespace() == Characters.EndTagMarkup && parent.Equals(elementName)) {
                        reader.ReadChar();
                        break;
                        }
                    else {
                        builder.Append(Characters.StartTagMarkup);
                        builder.Append(Characters.ClosingTagMarkup);
                        builder.Append(elementName);
                        }
                    }
                else
                    builder.Append(reader.CurrentChar);
                reader.ReadChar();
                }
            SElement cdata = parent.Nodes.Push();
            cdata.Clear();
            cdata.Parent = parent;
            cdata.LineNumber = context.LineNumber;
            cdata.LinePosition = context.LinePosition;
            if (parent.HasDeclaration && parent.Declaration.ContentModel.Groups.Count > 0)
                cdata.DName = parent.Declaration.ContentModel.Groups.FirstItem.Name;
            else
                cdata.DName = DName.CData;
            cdata.NodeType = SgmlNodeType.CData;
            
            // Set value
            cdata.Value = builder.ToString();
            builder.Clear();

            // Create and return end element
            SElement closing = parent.Nodes.Push();
            closing.Clear();
            closing.Parent = parent;
            closing.DName = parent.DName;
            closing.Name = parent.Name;
            closing.Declaration = parent.Declaration;
            closing.NodeType = SgmlNodeType.EndElement;
            return closing;
            }

        private void ReadEndCDataSection(SgmlStreamReader stream, SElement parent, Context context) {
            // Use multiple CDATA sections if the section contain the string ']]>'
            // In XML the ']]>' not allowed in CDATA element
            if (stream.CurrentChar == Characters.RightBracket && stream.PeekChar() == Characters.RightBracket) {
                stream.ReadChar();
                if (stream.ReadChar() == Characters.EndTagMarkup) {
                    builder.Append(Characters.RightBracket);
                    builder.Append(Characters.RightBracket);
                    builder.Append(Characters.EndTagMarkup);
                    // break;
                    }
                else {
                    builder.Append(Characters.RightBracket);
                    builder.Append(Characters.RightBracket);
                    builder.Append(stream.CurrentChar);
                    }
                }
            }
        }
    }
